/*
 * Copyright © 2018 www.noark.xyz All Rights Reserved.
 *
 * 感谢您选择Noark框架，希望我们的努力能为您提供一个简单、易用、稳定的服务器端框架 ！
 * 除非符合Noark许可协议，否则不得使用该文件，您可以下载许可协议文件：
 *
 *        http://www.noark.xyz/LICENSE
 *
 * 1.未经许可，任何公司及个人不得以任何方式或理由对本框架进行修改、使用和传播;
 * 2.禁止在本项目或任何子项目的基础上发展任何派生版本、修改版本或第三方版本;
 * 3.无论你对源代码做出任何修改和改进，版权都归Noark研发团队所有，我们保留所有权利;
 * 4.凡侵犯Noark版权等知识产权的，必依法追究其法律责任，特此郑重法律声明！
 */
package xyz.noark.codec.output;

import xyz.noark.codec.Codec;
import xyz.noark.codec.CodecConstant;
import xyz.noark.codec.CodedOutputStream;

import java.util.Arrays;

/**
 * @author 小流氓[176543888@qq.com]
 */
abstract class AbstractCodedOutputStream implements CodedOutputStream, CodecConstant {
    /**
     * 由 array growth 计算施加的软最大数组长度。
     * 某些 JVM（例如 HotSpot）具有实现限制，如果请求分配接近 Integer.MAX_VALUE 的某个长度的数组，
     * 则会导致引发 OutOfMemoryError（“请求的数组大小超出 VM 限制”），即使有足够的可用堆也是如此。
     * 实际限制可能取决于某些特定于 JVM 实现的特征，例如对象标头大小。
     * 软最大值是保守选择的，以便小于可能遇到的任何 implementation limit 。
     */
    public static final int SOFT_MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8;

    protected byte[] byteArray;
    protected int writeIndex;

    AbstractCodedOutputStream() {
        this.byteArray = new byte[Codec.config.getInitBufferSize()];
        this.writeIndex = 0;
    }

    /**
     * 确保当前缓冲区有足够的容量来容纳最小容量。
     *
     * @param minCapacity 最小容量
     */
    protected void ensureCapacity(int minCapacity) {
        if (minCapacity >= byteArray.length) {
            byteArray = grow(minCapacity);
        }
    }

    private byte[] grow(int minCapacity) {
        int oldCapacity = byteArray.length;
        // 将容量增加一半
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 确保新容量至少为 minCapacity
        if (newCapacity - minCapacity < 0) {
            newCapacity = minCapacity;
        }
        // 确保新容量不超过 SOFT_MAX_ARRAY_LENGTH
        if (newCapacity > SOFT_MAX_ARRAY_LENGTH) {
            throw new OutOfMemoryError("try enabling LargeObject feature instead");
        }
        // 创建一个新数组
        return Arrays.copyOf(byteArray, newCapacity);
    }

    @Override
    public void writeBoolean(boolean value) {
        this.writeLong(value ? 1 : 0);
    }

    @Override
    public void writeByte(byte value) {
        this.writeLong(value);
    }

    @Override
    public void writeShort(short value) {
        this.writeLong(value);
    }

    @Override
    public void writeInt(int value) {
        this.writeLong(value);
    }

    @Override
    public void writeLong(long value) {
        // 正数处理
        if (value >= 0) {
            // 1字节
            if (value <= int32_byte_1_max) {
                byteArray[writeIndex++] = (byte) value;
            } else if (value <= 0xFFF) {
                byteArray[writeIndex++] = (byte) (0x80 | (value >>> 8));
                byteArray[writeIndex++] = (byte) value;
            } else if (value <= 0xFFFF) {
                byteArray[writeIndex++] = (byte) 0x90;
                byteArray[writeIndex++] = (byte) (value >>> 8);
                byteArray[writeIndex++] = (byte) value;
            } else if (value <= 0xFFFFFF) {
                byteArray[writeIndex++] = (byte) 0x91;
                byteArray[writeIndex++] = (byte) (value >>> 16);
                byteArray[writeIndex++] = (byte) (value >>> 8);
                byteArray[writeIndex++] = (byte) value;
            } else if (value <= 0xFFFFFFFFL) {
                byteArray[writeIndex++] = (byte) 0x92;
                byteArray[writeIndex++] = (byte) (value >>> 24);
                byteArray[writeIndex++] = (byte) (value >>> 16);
                byteArray[writeIndex++] = (byte) (value >>> 8);
                byteArray[writeIndex++] = (byte) value;
            } else if (value <= 0xFFFF_FFFF_FFFFL) {
                byteArray[writeIndex++] = (byte) 0x93;
                byteArray[writeIndex++] = (byte) (value >>> 32);
                byteArray[writeIndex++] = (byte) (value >>> 24);
                byteArray[writeIndex++] = (byte) (value >>> 16);
                byteArray[writeIndex++] = (byte) (value >>> 8);
                byteArray[writeIndex++] = (byte) value;
            } else if (value <= 0xFF_FFFF_FFFF_FFFFL) {
                byteArray[writeIndex++] = (byte) 0x94;
                byteArray[writeIndex++] = (byte) (value >>> 40);
                byteArray[writeIndex++] = (byte) (value >>> 32);
                byteArray[writeIndex++] = (byte) (value >>> 24);
                byteArray[writeIndex++] = (byte) (value >>> 16);
                byteArray[writeIndex++] = (byte) (value >>> 8);
                byteArray[writeIndex++] = (byte) value;
            } else {
                byteArray[writeIndex++] = (byte) 0x95;
                byteArray[writeIndex++] = (byte) (value >>> 48);
                byteArray[writeIndex++] = (byte) (value >>> 40);
                byteArray[writeIndex++] = (byte) (value >>> 32);
                byteArray[writeIndex++] = (byte) (value >>> 24);
                byteArray[writeIndex++] = (byte) (value >>> 16);
                byteArray[writeIndex++] = (byte) (value >>> 8);
                byteArray[writeIndex++] = (byte) value;
            }
        }
        // 负数处理
        else {
            long negValue = -value - 1;  // 转换为正数
            if (negValue <= 0xFF) {
                byteArray[writeIndex++] = (byte) 0xFA;
                byteArray[writeIndex++] = (byte) negValue;
            } else if (negValue <= 0xFFFF) {
                byteArray[writeIndex++] = (byte) 0xFB;
                byteArray[writeIndex++] = (byte) (negValue >>> 8);
                byteArray[writeIndex++] = (byte) negValue;
            } else if (negValue <= 0xFFFFFF) {
                byteArray[writeIndex++] = (byte) 0xFC;
                byteArray[writeIndex++] = (byte) (negValue >>> 16);
                byteArray[writeIndex++] = (byte) (negValue >>> 8);
                byteArray[writeIndex++] = (byte) negValue;
            } else if (negValue <= 0xFFFFFFFFL) {
                byteArray[writeIndex++] = (byte) 0xFD;
                byteArray[writeIndex++] = (byte) (negValue >>> 24);
                byteArray[writeIndex++] = (byte) (negValue >>> 16);
                byteArray[writeIndex++] = (byte) (negValue >>> 8);
                byteArray[writeIndex++] = (byte) negValue;
            } else if (negValue <= 0xFFFF_FFFF_FFFFL) {
                byteArray[writeIndex++] = (byte) 0xFF;
                byteArray[writeIndex++] = (byte) (negValue >>> 40);
                byteArray[writeIndex++] = (byte) (negValue >>> 32);
                byteArray[writeIndex++] = (byte) (negValue >>> 24);
                byteArray[writeIndex++] = (byte) (negValue >>> 16);
                byteArray[writeIndex++] = (byte) (negValue >>> 8);
                byteArray[writeIndex++] = (byte) negValue;
            } else {
                throw new IllegalArgumentException("Unsupported negative value: " + value);
            }
        }
    }

    @Override
    public byte[] toByteArray() {
        return Arrays.copyOf(byteArray, writeIndex);
    }

    @Override
    public void writeString(String value) {

    }

    @Override
    public void writeBytes(byte[] value) {

    }

    @Override
    public void close() {

    }
}